#include "cledMatrixRGB.h"
#include "cledMatrix.h"
#include "UGUI/ugui.h"
#include "LCD/lcd.h"
#include "cledMatrixRGB.h"
#include "Curve/hsv.h"
#include <math.h>
#include <stdlib.h>

enum
{
    ZONE_TYPE_SINGLE,
    ZONE_TYPE_LINEAR,
    ZONE_TYPE_MATRIX
};

static hsv_t RandomHSVColor()
{
    hsv_t hsv;

    hsv.hue = rand() % 360;
    hsv.saturation = 255;
    hsv.value = 255;

    return hsv;
}
static uint32_t RandomRGBColor()
{
    hsv_t hsv = RandomHSVColor();
    return (hsv2rgb(&hsv));
}
static uint32_t Enlight(uint32_t color, float value)
{
    hsv_t hsv;
    rgb2hsv(color, &hsv);

    hsv.value *= value;

    return (hsv2rgb(&hsv));
}
static unsigned char InterpolateChanel(unsigned char a, unsigned char b, float x)
{
    return (int) ((b - a) * x + a);
}
#define RGBGetRValue(rgb)   (rgb & 0x000000FF)
#define RGBGetGValue(rgb)   ((rgb >> 8) & 0x000000FF)
#define RGBGetBValue(rgb)   ((rgb >> 16) & 0x000000FF)
#define ToRGBColor(r, g, b) ((uint32_t)((b << 16) | (g << 8) | (r)))
static uint32_t Interpolate(uint32_t color1, uint32_t color2, float fraction)
{
    return ToRGBColor(
                InterpolateChanel(RGBGetRValue(color1), RGBGetRValue(color2), fraction),
                InterpolateChanel(RGBGetGValue(color1), RGBGetGValue(color2), fraction),
                InterpolateChanel(RGBGetBValue(color1), RGBGetBValue(color2), fraction)
                );
}
static uint32_t Invert(uint32_t color)
{
    int r = (255-RGBGetRValue(color));
    int g = (255-RGBGetGValue(color));
    int b = (255-RGBGetBValue(color));

    return ToRGBColor(r, g, b);
}
static unsigned char ScreenChanel(unsigned char a, unsigned char b)
{
    return 255 - ((255 - b) * (255 - a) >> 8);;
}
static uint32_t Screen(uint32_t color1, uint32_t color2)
{
    return ToRGBColor(
                ScreenChanel(RGBGetRValue(color1), RGBGetRValue(color2)),
                ScreenChanel(RGBGetGValue(color1), RGBGetGValue(color2)),
                ScreenChanel(RGBGetBValue(color1), RGBGetBValue(color2))
                );
};

/*************************************************************************************************\
|        BouncingBallEffect [9]                                                                              |
\*************************************************************************************************/
#if 0
struct BouncingBallSimulation {
    //int zoneType;
    hsv_t baseColor;
    float hueDegrees;// = 0;
    float x, y,     // position
          dx, dy,   // velocity
          ddx, ddy, // acceleration
          dt;       // time step
    float impactVelocity;
    unsigned int fps;
    int width;
    int height;
    int numLeds;
    float radius;
    int dropHeightPercent;
    int spectrumVelocity;
};
static int radius;
static int gravity;
static int dropHeightPercent;
static int spectrumVelocity;
static int horizontalVelocity;
static struct BouncingBallSimulation bouncingBallSimulations;//[128];
//static uint16_t bouncingBallSize=0;
static float BouncingBallSimulation_GetGravity(int value)
{
    return value <= 10 ? value : (10 + pow(1.07, value));
}
static float getHorizontalVelocity(int value, float dx)
{
    return value * (dx < 0 ? -1 : 1);
}
static void BouncingBallSimulation_initSimulation(const uint32_t cols, const uint32_t rows, struct BouncingBallSimulation* Ball)
{
    float dropHeight = getDropHeight(Ball->dropHeightPercent, Ball->height-1);
    Ball->impactVelocity = getImpactVelocity(Ball->ddy, Ball->dropHeight);

    Ball->y = Ball->height - Ball->dropHeight;
    Ball->dy = 0;

    Ball->x = rndX(rng);
    Ball->dx = Ball->dx * (rndBool(rng) ? -1 : 1);

    oldLedIds.clear();
    newLedIds.clear();
    controllerZone->SetAllZoneLEDs(off, Brightness);
}

static void BouncingBallSimulation_Ball(const uint32_t cols, const uint32_t rows, struct BouncingBallSimulation* Ball,
    unsigned int fps,
    unsigned int radius,
    unsigned int gravity,
    unsigned int horizontalVelocity,
    unsigned int spectrumVelocity,
    unsigned int dropHeightPercent)
{
    //Ball->zoneIndex = 0;
    //Ball->zoneType = ZONE_TYPE_MATRIX;
    //Ball->startIndex = 0;

//    if (Ball->zoneType == ZONE_TYPE_MATRIX) {
        Ball->width = cols;
        Ball->height = rows;
//    } else {
//        Ball->height = cols*rows;
//        Ball->width = 1;
//    }

    Ball->numLeds = Ball->width * Ball->height;
    Ball->fps = fps;
    Ball->dt = 1.0f/fps;//getTimeDelta(fps);
    Ball->radius = radius;
    calculatePointsInCircle(radius);
    Ball->dropHeightPercent = dropHeightPercent;
    Ball->dx = getHorizontalVelocity(horizontalVelocity, Ball->dx);
    Ball->spectrumVelocity = spectrumVelocity;
    Ball->ddx = 0;
    Ball->ddy = BouncingBallSimulation_GetGravity(gravity);

    rng = std::default_random_engine(std::random_device{}());
    rndX = std::uniform_int_distribution<>(0, width-1); // [min, max] inclusive
    rndBool = std::uniform_int_distribution<>(0,1);

    Ball->baseColor.hue = 0;
    Ball->baseColor.saturation = 255;
    Ball->baseColor.value = 255;

    //initSimulation();
    BouncingBallSimulation_initSimulation(cols, rows, Ball);
}

static void BouncingBallSimulation_StepEffect(const uint32_t cols, const uint32_t rows, struct BouncingBallSimulation* Ball)
{
    // Prevent UI components from updating simulation parameters during step effect calculations
    //lockObj.lock();

    // Draw ball
    Ball->baseColor.hue = Ball->hueDegrees;
    newLedIds.clear();
    for (unsigned int i = 0; i < pointsInBall.size(); i++)
    {
        QPoint point = pointsInBall[i];
        int rx = point.x();
        int ry = point.y();
        int ledIndex = (int)(Ball->y + ry) * Ball->width + (int)(Ball->x + rx);

        // Skip points outside boundaries of LED matrix
        if (Ball->x + rx < 0 || x + rx >= Ball->width ||
            Ball->y + ry < 0 || y + ry >= Ball->height ||
            ledIndex < 0 || ledIndex >= Ball->numLeds) {
            continue;
        }

        // Apply brightness to pixel in proportion to distance of pixel from center of ball
        hsv_t pixelColor = Ball->baseColor;
        pixelColor.value = pointBrightness[i];

        // Set new LED color
//        unsigned int ledId = Ball->zoneType == ZONE_TYPE_MATRIX
//                ? (Ball->y * Ball->height) + Ball->x
//                : ledIndex;
        unsigned int ledId = (Ball->y * Ball->height) + Ball->x;
        ledId = ledIndex;
        //controllerZone->SetLED(startIndex + ledId, RGBColor(hsv2rgb(&pixelColor)), Brightness);
        ugui_pset(ledId/cols, ledId%cols, hsv2rgb(&pixelColor));

        // Update new and old LED indices
        newLedIds.insert(ledId);
        oldLedIds.erase(ledId);
    }

    // Turn off LEDs no longer within radius of ball
    for (const unsigned int& oldLedId : oldLedIds) {
        controllerZone->SetLED(startIndex + oldLedId, off, Brightness);
        ugui_pset(col_id, row_id, off);
    }

    // New LEDs changed this frame will be old LEDs next frame
    oldLedIds.swap(Ball->newLedIds);

    //if (Ball->zoneType == ZONE_TYPE_MATRIX)
    {
        // Update horizontal velocity and position of ball
        float dxp = dx + ddx * dt;
        float xp = x + dxp * dt;

        if (xp < 0) {
            float percentOvershoot = xp / (xp - x);
            dx = -dxp;
            x = dx * dt * percentOvershoot;
        } else if (xp >= width) {
            float overshoot = xp - width - 1;
            float percentOvershoot = overshoot / (xp - x);
            dx = -dxp;
            x = width - 1 + dx * dt * percentOvershoot;
        } else {
            x += dx * dt;
        }
    }

    // Update vertical velocity and position of ball
    float dyp = Ball->dy + ddy * Ball->dt;
    float yp = Ball->y + dyp * Ball->dt;

    if (yp >= Ball->height) {
        float overshoot = yp - Ball->height - 1;
        float percentOvershoot = overshoot / (yp - Ball->y);
        Ball->dy = -Ball->impactVelocity + Ball->ddy * Ball->dt * percentOvershoot;
        Ball->y = Ball->height - 1 + Ball->dy * Ball->dt * percentOvershoot;
    } else {
        Ball->dy += Ball->ddy * Ball->dt;
        Ball->y += Ball->dy * Ball->dt;
    }

    // Update color
    Ball->hueDegrees += spectrumVelocity * Ball->dt;
    if (Ball->hueDegrees >= 360) {
        Ball->hueDegrees = 0;
    }

    //lockObj.unlock();
}
void cledMatrixBouncingBallEffect_Init(void)    // 弹力球
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 20;
    mParams.Slider2Val = 30;
    mParams.shape = 0;
    mParams.progress = 0.f;
    mParams.background = 0x00;
    radius = 5;//ui->ball_radius_slider->value();
    gravity = 10;//ui->gravity_slider->value();
    horizontalVelocity = 10;//ui->horizontal_velocity_slider->value();
    spectrumVelocity = 10;//ui->spectrum_velocity_slider->value();
    dropHeightPercent = 90;//ui->drop_height_percent_slider->value();
    BouncingBallSimulation_Ball(
                    41, 41, &bouncingBallSimulations,
                    mParams.FPS,
                    radius,
                    gravity,
                    horizontalVelocity,
                    spectrumVelocity,
                    dropHeightPercent);
}
static void BouncingBall_checkForDimensionChange(const uint32_t cols, const uint32_t rows, struct BouncingBallSimulation* bouncingBallSim)
{
    //if (controllerZone->type() == ZONE_TYPE_MATRIX)
    {
        // Manually check for on-the-fly changes in matrix width or height.
        // Width and height are not updated via slots through the UI.
        int newWidth = cols;//controllerZone->matrix_map_width();
        if (bouncingBallSim->width != newWidth) {
            bouncingBallSim->width = (newWidth);
        }

        int newHeight = rows;//controllerZone->matrix_map_height();
        if (bouncingBallSim->height != newHeight) {
            bouncingBallSim->height = (newHeight);
        }
    }
}
void cledMatrixBouncingBallEffect(const uint32_t cols, const uint32_t rows)    // 弹力球
{
//    BouncingBallSimulation* bouncingBallSim;
//    auto kvp = bouncingBallSimulations.find(controller_zone);

//    if (kvp != bouncingBallSimulations.end()) {
//        bouncingBallSim = kvp->second;
        BouncingBall_checkForDimensionChange(cols, rows, &bouncingBallSimulations);
//    } else {
//        bouncingBallSim = new BouncingBallSimulation(
//            controller_zone,
//            mParams.FPS,
//            radius,
//            gravity,
//            horizontalVelocity,
//            spectrumVelocity,
//            dropHeightPercent);

//        std::pair<ControllerZone*, BouncingBallSimulation*> pair(controller_zone, bouncingBallSim);
//        bouncingBallSimulations.insert(pair);
//    }

    //bouncingBallSim->StepEffect();
    BouncingBallSimulation_StepEffect(cols, rows, &bouncingBallSimulations);
}
#else
/*************************************************************************************************\
|        BouncingBallEffect [9]                                                                              |
\*************************************************************************************************/
struct BouncingBall{
    double x,y;
};
static int radius;
static int gravity;
static int dropHeightPercent;
static int spectrumVelocity;
static int horizontalVelocity;
static struct BouncingBall Ball;
void cledMatrixBouncingBallEffect_Init(void)    // 弹力球
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 50;
    mParams.Slider2Val = 30;
    mParams.shape = 0;
    mParams.progress = 0.f;
    mParams.background = 0x00;
    radius = 3;//ui->ball_radius_slider->value();
    gravity = 10;//ui->gravity_slider->value();
    horizontalVelocity = 70;//ui->horizontal_velocity_slider->value();
    spectrumVelocity = 50;//ui->spectrum_velocity_slider->value();
    dropHeightPercent = 90;//ui->drop_height_percent_slider->value();
    Ball.x = 0;
    Ball.y = 0;
}
static uint32_t BouncingBall_GetColor(unsigned int x, unsigned int y, double cx, double cy, int reverse)
{
    //float direction_mult = -1.0f;//direction == 0 ? 1.f : -1.f;
    double angle;
    struct cledMatrixPixel piont;
    float hue;// = (float)(mParams.progress + (int)(180 + direction_mult * (reverse ? atan2(y - cy, x - cx) : atan2(x - cx, y - cy)) * (180.0 / 3.14159)) % 360);
    piont.pixel = cledMatrixAngleLimitGet((x - cx), (y - cy));
    angle = piont.angle/8.0f;
    if(piont.radius >= radius) return 0x00;
//    if(piont.radius <1) return 0x00;
    //hue = (float)(mParams.progress + (int)(180 + direction_mult * (reverse ? atan2(y - cy, x - cx) : atan2(x - cx, y - cy)) * (180.0 / 3.14159)) % 360);
    hue = (float)(mParams.progress + (int)(360 + /*direction_mult **/(reverse ? angle : 90.0f-angle)) % 360);
    //hue = (float)(mParams.progress + (int)(180 + (atan2(x - cx, y - cy)) * (180.0 / 3.14159)) % 360);
    //hue = (float)(mParams.progress + (int)(360 - angle) % 360);
    hsv_t hsv = { 0, 0, 0 };
    hsv.hue = (int)hue;
    hsv.saturation = 255;
    hsv.value = 255;
    //if(piont.radius>0) hsv.value = (255+128)/piont.radius;
    hsv.value = (255+255)/(piont.radius+2);

    return (hsv2rgb(&hsv));
}
void cledMatrixBouncingBallEffect(const uint32_t cols, const uint32_t rows)    // 弹力球
{
//    float cx_shift_mult = 0.5f;//cx_shift / 100.f;
//    float cy_shift_mult = 0.5f;//cy_shift / 100.f;
//    double cx = (cols - 1) * cx_shift_mult;
//    double cy = (rows - 1) * cy_shift_mult;
    double cx = sin(Ball.x)*410;
    double cy = sin(Ball.y)*410;

    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
         for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
            uint32_t color = BouncingBall_GetColor(col_id, row_id, cx/10.0f, cy/10.0f, 0);
            ugui_pset(col_id, row_id, color);
        }
    }
    mParams.progress += (float) mParams.Speed / (float) mParams.FPS;
    //mParams.time +=  0.01 * mParams.Speed / (float) mParams.FPS;
    Ball.x +=  0.01 * spectrumVelocity/(float) mParams.FPS;
    Ball.x +=  0.001 *cx/(float) mParams.FPS;
    Ball.y +=  0.01 * horizontalVelocity/(float) mParams.FPS;
    Ball.y +=  0.001 *cy/(float) mParams.FPS;
    if(Ball.x>=3.1415926) Ball.x=0;
    if(Ball.y>=3.1415926) Ball.y=0;
}
#endif
/*************************************************************************************************\
|        Bubbles [9]                                                                              |
\*************************************************************************************************/
static double    speeds[128];
//static uint16_t  speedsSize=0;
static double    bubbles[128];
static uint16_t  bubblesSize=0;
static uint32_t  colors[128];
//static uint16_t  colorsSize=0;
static unsigned int max_bubbles = 60;
static unsigned int rarity = 20;
static unsigned int speed_mult = 10;
static unsigned int max_expansion = 100;
static unsigned int bubbles_thickness = 10;
struct {
    double x, y;
} centers[128];
void cledMatrixBubbles_Init(void)    // 气泡
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 20;
    mParams.Slider2Val = 30;
    mParams.shape = 0;
    mParams.progress = 0.f;
    mParams.background = 0x00;
}
static uint32_t Bubbles_GetColor(int x, int y, int w, int h)
{
    int color_idx = -1;
    int val = 0;

    for(unsigned int i = 0; i < bubblesSize; i++)
    {
        double distance = sqrt(pow(w * centers[i].x - x, 2) + pow(h * centers[i].y - y, 2));
        double shallow = fabs(distance - bubbles[i]) / ( 0.1 * bubbles_thickness);
        //double value = std::min<double>(255, 255 * (1 / pow(shallow, 2)));
        double value = 255;
        if(value>(255 * (1 / pow(shallow, 2)))) value = 255 * (1 / pow(shallow, 2));

        if(value > val)
        {
            color_idx = i;
            val = value;
        }
    }

    if(color_idx >= 0)
    {
        hsv_t final;

        rgb2hsv(colors[color_idx], &final);

        final.value = val;
        return /*ColorUtils::*/Screen(hsv2rgb(&final), mParams.background);
    }
    else
    {
        return mParams.background;
    }

}
static void Bubbles_InitBubble()
{
    bubbles[bubblesSize]=0;

    double speed = 1 + 10 * (((double)rand() / RAND_MAX) - 1) *(-1);
    speeds[bubblesSize]=(speed);

    uint32_t color;

//    if(RandomColorsEnabled)
//    {
//        color = ColorUtils::RandomRGBColor();
//    }
//    else
//    {
//        color = UserColors[rand() % UserColors.size()];
//    }
    color = /*ColorUtils::*/RandomRGBColor();

    colors[bubblesSize]=(color);

    centers[bubblesSize].x = (((double)rand() / RAND_MAX) - 1) *(-1);
    centers[bubblesSize].y = (((double)rand() / RAND_MAX) - 1) *(-1);
    //centers.push_back(QPointF(x, y));
    bubblesSize++;
}

static void Bubbles_Cleanup()
{
    for(unsigned int i = 0; i < bubblesSize; i++)
    {
        if(bubbles[i] > max_expansion)
        {
//            bubbles.erase(bubbles.begin() + i);
//            centers.erase(centers.begin() + i);
//            speeds.erase(speeds.begin() + i);
//            colors.erase(colors.begin() + i);
            for(uint32_t kk = i; kk < bubblesSize-1; kk++)
            {
                memcpy(&bubbles[kk], &bubbles[kk+1], sizeof(bubbles[0]));
                memcpy(&centers[kk], &centers[kk+1], sizeof(centers[0]));
                memcpy(&speeds[kk], &speeds[kk+1], sizeof(speeds[0]));
                memcpy(&colors[kk], &colors[kk+1], sizeof(colors[0]));
            }
        }
    }
}

void cledMatrixBubbles(const uint32_t cols, const uint32_t rows)    // 气泡
{
    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
            uint32_t color = Bubbles_GetColor(col_id, row_id, cols, rows);
            //int LedID = controller_zone->controller->zones[controller_zone->zone_idx].matrix_map->map[((row_id * cols) + col_id)];
            //int LedID = (row_id * cols) + col_id;
            //controller_zone->SetLED(start_idx + LedID, color, Brightness);
            ugui_pset(col_id, row_id, color);
        }
    }
    for(unsigned int i = 0; i < bubblesSize; i++)
    {
        bubbles[i] += 0.2 * speed_mult * speeds[i] / (float) mParams.FPS;
    }


    if(rand() % rarity == 0 && bubblesSize < max_bubbles)
    {
        Bubbles_InitBubble();
    }

    Bubbles_Cleanup();
}
/*************************************************************************************************\
|        FractalMotion [9]                                                                              |
\*************************************************************************************************/
static uint32_t random_color;
static uint32_t next_random_color;
static unsigned int val_mult = 100;
static float inv_val_mult = 0.01f;//1.f / val_mult;
static float random_tick = 0.f;
//static uint32_t background = 0x00;//ColorUtils::OFF();
static unsigned int thickness = 2;
static float amplitude = 6.f;
static float frequency = 30.f;
static float freq_m1 = 2.1;
static float freq_m2 = 4.5;
static float freq_m3 = 1.72;
static float freq_m4 = 1.121;
static float freq_m5 = 4.0;
static float freq_m6 = 2.221;
static float freq_m7 = 0.437;
static float freq_m8 = 5.0;
static float freq_m9 = 2.1122;
static float freq_m10 = 1.5;
static float freq_m11 = 1.72;
static float freq_m12 = 0.06;

void cledMatrixFractalMotion_Init(void)    // 分形运动
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 50;
    mParams.Slider2Val = 30;
    mParams.shape = 0;
    mParams.progress = 0.f;
    mParams.background = 0x00;
    random_color = RandomRGBColor();
    next_random_color = RandomRGBColor();
}
static uint32_t FractalMotion_GetColor(unsigned int x, unsigned int ly, unsigned int h)
{
    float f = frequency * 0.01;
    float y = sin(x * f);
    float t = 0.01*(-mParams.progress*mParams.Speed);
#if 1
    y += sin(x*f*freq_m1 + t)*freq_m2;
    y += sin(x*f*freq_m3 + t*freq_m4)*freq_m5;
    y += sin(x*f*freq_m6 + t*freq_m7)*freq_m8;
    y += sin(x*f*freq_m9+ t*freq_m10)*freq_m11;
    y *= 0.1 * amplitude*freq_m12;
#else
    y += sin(x*f*freq_m1 + t)*freq_m2;
    y *= 0.1 * amplitude*freq_m12;
#endif

    y = (1 + y) * 0.5 * h;

    float distance = fabs(y - ly);

    if(distance > thickness)
    {
        return mParams.background;
    }

    uint32_t color;

//    if(RandomColorsEnabled)
//    {
//        color = ColorUtils::Interpolate(random_color, next_random_color, std::min<float>(1.f, random_tick));
//    }
//    else
//    {
//        color = UserColors[0];
//    }
    //color = /*ColorUtils::*/Interpolate(random_color, next_random_color, std::min<float>(1.f, random_tick));
    if(1.f< random_tick) color = /*ColorUtils::*/Interpolate(random_color, next_random_color, 1.0f);
    else color = /*ColorUtils::*/Interpolate(random_color, next_random_color, random_tick);

    return /*ColorUtils::*/Interpolate(color, mParams.background, distance / thickness);
}
void cledMatrixFractalMotion(const uint32_t cols, const uint32_t rows)    // 分形运动
{
    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
            //RGBColor color = GetColor(col_id, row_id, rows);
            uint32_t color = FractalMotion_GetColor(col_id, row_id, rows);
            //int LedID = controller_zone->controller->zones[controller_zone->zone_idx].matrix_map->map[((row_id * cols) + col_id)];
            //int LedID = (row_id * cols) + col_id;
            //controller_zone->SetLED(start_idx + LedID, color, Brightness);
            ugui_pset(col_id, row_id, color);
        }
    }
    float delta = (float) mParams.Speed / (float) mParams.FPS;

    if(random_tick >= 1)
    {
        random_color = next_random_color;
        next_random_color = /*ColorUtils::*/RandomRGBColor();
        random_tick = 0;
    }

    random_tick += 0.005 * delta;
    mParams.progress += 0.1 * delta;
}
/*************************************************************************************************\
|        GradientWave [9]                                                                              |
\*************************************************************************************************/
static int spread = 0;
void cledMatrixGradientWave_Init(void)    // 梯度波
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 25;
    mParams.Slider2Val = 30;
    mParams.shape = 0;
    mParams.progress = 0.f;
}
static uint32_t CustomGradientWave_GetColor(float x, float w)
{
    float i = (spread/100.f) * x / w + mParams.progress;
    i -= (long) i;
    hsv_t HSVVal;
    HSVVal.saturation = 255;
    HSVVal.value      = 255;
    HSVVal.hue = ((int)(360.0 * i))%360;
    //return /*ColorUtils::*/fromQColor(gradient.pixelColor(100.0 * i, 0));
    return hsv2rgb(&HSVVal);
}
void cledMatrixGradientWave(const uint32_t cols, const uint32_t rows)    // 梯度波
{
    int reverse=0;
    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
        uint32_t color = CustomGradientWave_GetColor(reverse ? cols - col_id - 1: col_id, cols);

        for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
            //int LedID = controller_zones[i]->controller->zones[controller_zones[i]->zone_idx].matrix_map->map[((row_id * cols) + col_id)];
            //int LedID = (row_id * cols) + col_id;
            //controller_zone->SetLED(start_idx + LedID, color, Brightness);
            ugui_pset(col_id, row_id, color);
        }
    }
    mParams.progress += 0.01 * mParams.Speed / (float) mParams.FPS;
}
/*************************************************************************************************\
|        MovingPanes [9]                                                                              |
\*************************************************************************************************/
static uint32_t UserColors[2];
void cledMatrixMovingPanes_Init(void)    // 移动窗格
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 30;
    mParams.Slider2Val = 4;
    mParams.shape = 0;
    mParams.progress = 0.f;
    UserColors[0] = RandomRGBColor();
    UserColors[1] = RandomRGBColor();
}
static uint32_t MovingPanes_GetColor(float x, float y, float w, float h, int reverse)
{
    mParams.Slider2Val = 10;
    int zone = x / (w / mParams.Slider2Val);
    int zone_id = zone % 2;
    float pi4 = 3.14 * 0.15;
    float t = reverse ? mParams.time : -mParams.time;

    float s = 0.5 * (1 + sin((y) / (h * 0.25) + (zone_id ? 1 : -1) * t + pi4));

    return /*ColorUtils::*/Interpolate(UserColors[zone_id ? 1 : 0], UserColors[zone_id ? 0 : 1], s);
}
void cledMatrixMovingPanes(const uint32_t cols, const uint32_t rows)    // 移动窗格
{
    int reverse=1;
    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
            //RGBColor color = GetColor(col_id, row_id, cols, rows, reverse);
            uint32_t color = MovingPanes_GetColor(row_id, col_id, cols, rows, reverse);

            //int LedID = controller_zones[i]->controller->zones[controller_zones[i]->zone_idx].matrix_map->map[((row_id * cols) + col_id)];
            //int LedID = (row_id * cols) + col_id;
            //controller_zone->SetLED(start_idx + LedID, color, Brightness);
            ugui_pset(row_id, col_id, color);
        }
    }
    mParams.time += 0.1 * (float) mParams.Speed / (float) mParams.FPS;
}
/*************************************************************************************************\
|        Rain [9]                                                                              |
\*************************************************************************************************/
struct Drop
{
    double progress;
    uint32_t color;
    unsigned int col;
    float speed_mult;
};
struct Drop drops[41];
static uint16_t dropsSize=0;
void cledMatrixRain_Init(void)    // 下雨
{
//    mParams.time = 1000.0;
//    mParams.FPS = 60;
    mParams.Speed = 100;
    mParams.Slider2Val = 10;
//    mParams.shape = 0;
//    mParams.progress = 0.f;
}
static uint32_t Rain_GetColor(unsigned int x, unsigned int y)
{
    for(uint16_t i=0; i<dropsSize; i++)
    {
        if(drops[i].col == x)//if(drop.col == x)
        {
            //float distance = drop.progress - y;
            float distance = drops[i].progress - y;

            if(distance >= 0 && distance < 3)
            {
                float whole;
                float frac = /*std::*/modf(distance, &whole);

                //qDebug("[%s--%d] drop.col:%2d distance:%8f frac:%8f", __func__, __LINE__, drop.col, distance, frac);
                if (whole == 2) // tail
                {
                    return /*ColorUtils::*/Enlight(drops[i].color, 1 - frac);
                }
                else if(whole == 1) // middle
                {
                    return drops[i].color;
                }
                else // head
                {
                    return /*ColorUtils::*/Enlight(drops[i].color, frac);
                }
            }
        }
    }

    return 0x00;//ColorUtils::OFF();
}
static void Rain_TriggerDrop(unsigned int w)
{
    //unsigned int max_drops = std::min(w, Slider2Val);
    unsigned int max_drops = w;
    if(max_drops>mParams.Slider2Val) max_drops=mParams.Slider2Val;

    if(dropsSize < max_drops && (rand() % (2 + mParams.FPS / w) == 0))
    {
        uint32_t color;

        /*if(OnlyFirstColorEnabled)
        {
            color = UserColors[0];
        }
        else if(RandomColorsEnabled)
        {
            color = ColorUtils::RandomRGBColor();
        }
        else
        {
            color = UserColors[(rand() % UserColors.size())];
        }*/
        color = /*ColorUtils::*/RandomRGBColor();

        unsigned int col = rand() % w;   // 通过随机数生成列
//        float speed_mult = 1 + static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
        float speed_mult = 1 + (float)rand() / ((float)RAND_MAX);
        struct Drop drop = {0.f, color, col, speed_mult};
        //drops[controller_zone_index].push_back(drop);
        drops[dropsSize]=drop;
        dropsSize++;

    }
}

static void Rain_RunDrops()
{
    //for(Drop& drop: drops[controller_zone_index])
    for(uint16_t i=0; i<(sizeof(drops)/sizeof(drops[0])); i++)
    {
        drops[i].progress += 0.1 * drops[i].speed_mult * mParams.Speed / (float) mParams.FPS;
    }
}

static void Rain_CleanDrops(unsigned int h)
{
//    for( std::vector<Drop>::iterator iter = drops[controller_zone_index].begin();
//         iter != drops[controller_zone_index].end();)
//    {
//        if(iter->progress > h + 3)  // 一个雨滴由三个点构成
//        {
//            drops[controller_zone_index].erase(iter);
//        }
//        else
//        {
//            iter++;
//        }
//    }
    struct Drop* iter=NULL;
    for( uint16_t i=0; i<dropsSize; i++)
    {
        iter=&drops[i];
        if(iter->progress > h + 3)  // 一个雨滴由三个点构成
        {
            if(dropsSize<=0) break;
            for( uint16_t kk=i; kk<(dropsSize-1); kk++)
            {
                memcpy(&drops[kk], &drops[kk+1], sizeof(drops[00]));
            }
            dropsSize--;
        }
        else
        {
            iter++;
        }
    }
}
void cledMatrixRain(const uint32_t cols, const uint32_t rows)    // 下雨
{
    unsigned int w;
    unsigned int h;
    const int reverse=0;
    w = cols;
    h = rows;

    //qDebug("\r\n[%s--%d] ", __func__, __LINE__);
    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
            //int LedID = controller_zones[i]->controller->zones[controller_zones[i]->zone_idx].matrix_map->map[((row_id * cols) + col_id)];
            //int LedID = (row_id * cols) + col_id;
            //int LedID = (col_id * rows) + row_id;
            //RGBColor color = GetColor(i, col_id, reverse ? rows - row_id - 1: row_id);
            uint32_t color = Rain_GetColor(col_id, reverse ? rows - row_id - 1: row_id);
            //controller_zone->SetLED(start_idx + LedID, color, Brightness);
            ugui_pset(col_id, row_id, color);
        }
    }
    Rain_TriggerDrop(w);
    Rain_RunDrops();
    Rain_CleanDrops(h);
}
/*************************************************************************************************\
|        SmoothBlink [9]                                                                              |
\*************************************************************************************************/
static uint32_t current_color_1;
static uint32_t current_color_2;
static uint32_t random_color_1;
static uint32_t random_color_2;
static uint32_t next_color_1;
static uint32_t next_color_2;
static float pulses_total_duration;
static float total_effect_duration;
static float value = 1.0;
static double random_fade_timer = 0;
enum PulseRendering {
    Solid = 0,
    Circle = 1
};

static double pi = 3.14159265358979323846;

//float default_interval = 2.0;
//float default_strength = 0.5;
//unsigned int default_pulses = 2;
//float default_pulse_duration = 0.5;
//enum PulseRendering default_rendering = Solid;

static float interval = 2.0;
static float strength = 0.5;
static unsigned int pulses = 2;
static float pulse_duration = 0.5;
static enum PulseRendering rendering = Solid;

void cledMatrixSmoothBlink_Init(void)    // 平滑闪烁
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 20;
    mParams.Slider2Val = 30;
    mParams.shape = 0;
    mParams.progress = 0.f;
    random_color_1 = RandomRGBColor();
    random_color_2 = RandomRGBColor();
    next_color_1 = RandomRGBColor();
    next_color_2 = RandomRGBColor();
}
static uint32_t SmoothBlink_GetColor(unsigned int x, unsigned int y, double cx, double cy, unsigned int max_distance)
{
    if(value >= 1)
    {
        return current_color_2;
    }

    double distance = sqrt(pow(cx - x, 2) + pow(cy - y, 2));
    float distance_percent = distance / max_distance;

    //return Interpolate(current_color_1, current_color_2, std::min<double>(1,value + distance_percent));
    if(1>(value + distance_percent)) return Interpolate(current_color_1, current_color_2, value + distance_percent);
    else return Interpolate(current_color_1, current_color_2, 1);
}
static void SmoothBlink_HandleSolidRendering(const uint32_t cols, const uint32_t rows)
{
    uint32_t color = Interpolate(current_color_1, current_color_2, value);

    //for(ControllerZone* controller_zone : controller_zones)
//    {
//        controller_zone->SetAllZoneLEDs(color, Brightness);
//    }
    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
           ugui_pset(col_id, row_id, color);
        }
    }
}

static void SmoothBlink_HandleCircleRendering(const uint32_t cols, const uint32_t rows)
{
    float cx_shift_mult = 0.5f;//cx_shift / 100.f;
    float cy_shift_mult = 0.5f;//cy_shift / 100.f;

    //for(ControllerZone* controller_zone : controller_zones)
    {
        unsigned int width = cols;
        unsigned int height = rows;
        //unsigned int * map = controller_zone->map();

        float cx = (width - 1) * cx_shift_mult;
        float cy = (height - 1) * cy_shift_mult;

        for(unsigned int h = 0; h < height; h++)
        {
            for(unsigned int w = 0; w <  width; w++)
            {
                uint32_t color = SmoothBlink_GetColor(w, h, cx, cy, width + height);

                //unsigned int led_num = map[h * width + w];
                //controller_zone->SetLED(start_idx + led_num, color, Brightness);
                ugui_pset(w, h, color);
            }
        }
    }
}
void cledMatrixSmoothBlink(const uint32_t cols, const uint32_t rows)    // 平滑闪烁
{
    //if(RandomColorsEnabled)
    {
        if(random_fade_timer <= 0.5 * interval)
        {
            current_color_1 = /*ColorUtils::*/Interpolate(random_color_1, next_color_1, random_fade_timer / (0.5 * interval));
            current_color_2 = /*ColorUtils::*/Interpolate(random_color_2, next_color_2, random_fade_timer / (0.5 * interval));
        }
        else
        {
            random_color_1 = next_color_1;
            random_color_2 = next_color_2;
        }
    }
//    else
//    {
//        current_color_1 = UserColors[0];
//        current_color_2 = UserColors[1];
//    }

    pulses_total_duration = pulses * pulse_duration;
    total_effect_duration = interval + pulses_total_duration;

    float s = 0.5 + (1 - strength) * 0.5;

    if(mParams.time < interval)
    {
        value = 1.0;
    }
    else
    {
        float x = mParams.time - interval;
        float y = 0.5 * (1 + sin(2 * pulses / pulses_total_duration * x * pi - 0.5 * pi));
        value = y - (y - s) / s;
    }

    switch (rendering) {
    case Solid:
        SmoothBlink_HandleSolidRendering(cols, rows);
        break;

    case Circle:
        SmoothBlink_HandleCircleRendering(cols, rows);
        break;

    default: break;
    }

    mParams.time += 1.0 / (float) mParams.FPS;
    random_fade_timer += 1.0 / (float) mParams.FPS;

    if(mParams.time >= total_effect_duration)
    {
        mParams.time = mParams.time - total_effect_duration;
        next_color_1 = /*ColorUtils::*/RandomRGBColor();
        next_color_2 = /*ColorUtils::*/RandomRGBColor();
        random_fade_timer = 0;
    }
}

/*************************************************************************************************\
|        Spiral [9]                                                                              |
\*************************************************************************************************/
uint32_t Spiral_GetColor(float x, float y, float cx, float cy, int reverse)
{
    double angle    = (reverse ? atan2(x - cx, y - cy) :  - atan2(x - cx, y - cy)) * 180 / 3.14159265359;
    double distance = sqrt(pow(cx - x, 2) + pow(cy - y, 2));

    hsv_t hsv;

    //if(RandomColorsEnabled)
    {
        hsv.value = 255;
        hsv.hue = abs((int)(angle + mParams.Slider2Val * distance - mParams.time) % 360);
        hsv.saturation = 255;
    }
//    else
//    {
//        rgb2hsv(UserColors[0], &hsv);
//        hsv.value = 255 - (abs((int)(angle + Slider2Val * distance - time) % 360) / 360.f) * 255;
//    }

    return (hsv2rgb(&hsv));
}
uint32_t Spiral_GetColor2(float x, float y, float cx, float cy, int reverse)
{
//    double angle    = (reverse ? atan2(x - cx, y - cy) :  - atan2(x - cx, y - cy)) * 180 / 3.14159265359;
//    double distance = sqrt(pow(cx - x, 2) + pow(cy - y, 2));
    double angle;
    double distance;
    struct cledMatrixPixel piont;
    //struct cledMatrixPixelLimit piontLimit;
    piont.pixel = cledMatrixAngleGet((x - cx), (y - cy));
    piont.pixel = cledMatrixAngleLimitGet((x - cx), (y - cy));
    angle = piont.angle;///64.0f;
    distance = piont.radius;///64.0f;
//    angle = piontLimit.angle;///64.0f;
//    distance = piontLimit.radius;///64.0f;
    hsv_t hsv;

    //if(RandomColorsEnabled)
    {
        hsv.value = 255;
        //hsv.hue = abs((int)(angle + Slider2Val * distance - time) % 360);
        //hsv.hue = abs((int)((angle/8.0f + Slider2Val * distance/2.0f - time)) % 360);
        // 螺旋
        //hsv.hue = abs((int)((piont.angle + ((Slider2Val * piont.radius)<<3) - ((uint32_t)time<<3))>>3) % 360);
        // 七彩风车
        hsv.hue = abs((int)((piont.angle + ((mParams.Slider2Val * piont.radius)<<1) - ((uint32_t)mParams.time<<3))>>2) % 360);
        //hsv.hue = abs((int)((angle/8.0f + Slider2Val * distance - time)) % 360);
        //hsv.hue = abs((int)((piontLimit.angle/8.0f + Slider2Val * piontLimit.radius/2.0f - time)) % 360);
        hsv.saturation = 255;
    }
//    else
//    {
//        rgb2hsv(UserColors[0], &hsv);
//        hsv.value = 255 - (abs((int)(angle + Slider2Val * distance - time) % 360) / 360.f) * 255;
//    }

    return (hsv2rgb(&hsv));
}
void cledMatrixSpiral_Init(void)
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 100;
    mParams.Slider2Val = 10;
}
void cledMatrixSpiral(const uint32_t cols, const uint32_t rows)    // 螺旋效果
{
    float cx = (cols - 1) * 0.5;
    float cy = (rows - 1) * 0.5;

    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
            uint32_t color = Spiral_GetColor2(col_id, row_id, cx, cy, 0);

            //int LedID = controller_zones[i]->controller->zones[controller_zones[i]->zone_idx].matrix_map->map[((row_id * cols) + col_id)];
            //int LedID = (row_id * cols) + col_id;
            //controller_zone->SetLED(start_idx + LedID, color, Brightness);
            ugui_pset(col_id, row_id, color);
        }
    }
    mParams.time += mParams.Speed / (float) mParams.FPS;
}

/*************************************************************************************************\
|        StarryNight [9]                                                                              |
\*************************************************************************************************/
struct NewStar
{
    unsigned int        Index;
    unsigned int        LED;
    float               state;
    uint32_t            Color;
};
static struct NewStar CurrentStars[20];
static uint16_t CurrentStarsSize=0;
void cledMatrixStarryNight_Init(void)    // 星空
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 150;
    mParams.Slider2Val = 20;
    mParams.shape = 0;
    mParams.progress = 0.f;
    const uint32_t cols=41;
    const uint32_t rows=41;
    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
           ugui_pset(col_id, row_id, 0x00);
        }
    }
}
void cledMatrixStarryNight(const uint32_t cols, const uint32_t rows)    // 星空
{
    uint32_t LEDPerCycle = mParams.Slider2Val;

    //int AmountMadeThisCycle = 0;

    unsigned int ControllerID = 0;
    //for (unsigned int ControllerID = 0; ControllerID < controller_zones.size(); ControllerID++)
    {
        // Note: this rand() call is the cause, see comment at the top of the file
        // CurrentStars should be a map <ControllerZone*, std::vector<NewStar>>
        // also NewStar should be renamed to "Star" or something more explicit.
        if (rand() % 2)
        {
            int MakeForZone = 0;

            if ((LEDPerCycle - CurrentStarsSize) > 0)
            {
                MakeForZone = rand() % (LEDPerCycle - CurrentStarsSize);
            }

            if ((CurrentStarsSize < LEDPerCycle))
            {
                for (int ZonesMade = 0; ZonesMade < MakeForZone; ZonesMade++)
                {
                    int StartingLED = 0;//controller_zone->start_idx();
                    int RandomLedID = rand() % (41*41);//controller_zone->leds_count();

                    struct NewStar* const LEDStar = &CurrentStars[CurrentStarsSize++];
                    LEDStar->Index = ControllerID;
                    LEDStar->LED = (StartingLED + RandomLedID);
                    LEDStar->state = 255;

//                    if (RandomColorsEnabled)
//                    {
//                        LEDStar.Color = ColorUtils::RandomRGBColor();
//                    }
//                    else
//                    {
//                        LEDStar.Color = UserColors[rand() % UserColors.size()];
//                    }
                    LEDStar->Color = /*ColorUtils::*/RandomRGBColor();

                    //CurrentStars.push_back(LEDStar);
                    //AmountMadeThisCycle += 1;
                }
            }

        }
    }

    uint32_t ToBeDeleted[100];
    uint16_t ToBeDeletedSize=0;

    for (uint32_t StarIndex = 0; StarIndex < CurrentStarsSize; StarIndex++)
    {
        //unsigned int CTRLR = CurrentStars[StarIndex].Index;

        //if(CTRLR < controller_zones.size())
        //if(CTRLR < 1)
        {
            hsv_t SetColor;
            rgb2hsv(CurrentStars[StarIndex].Color,&SetColor);

            float NewValue = (CurrentStars[StarIndex].state - ((float)mParams.Speed / (float)(1000/(float)mParams.FPS)));

            if ((NewValue < 1) || (NewValue > 255))
            {
                //ToBeDeleted.push_back(StarIndex);
                ToBeDeleted[ToBeDeletedSize++] = (StarIndex);
                SetColor.value = 0;
                //controller_zone->SetLED(CurrentStars[StarIndex].LED,hsv2rgb(&SetColor), Brightness);
                ugui_pset(CurrentStars[StarIndex].LED/cols, CurrentStars[StarIndex].LED%cols, hsv2rgb(&SetColor));
            }
            else
            {
                SetColor.value = CurrentStars[StarIndex].state;
                //controller_zone->SetLED(CurrentStars[StarIndex].LED,hsv2rgb(&SetColor), Brightness);
                ugui_pset(CurrentStars[StarIndex].LED/cols, CurrentStars[StarIndex].LED%cols, hsv2rgb(&SetColor));
                CurrentStars[StarIndex].state -= ((mParams.Speed) / (mParams.FPS) );
            }
        }
//        else
//        {
//             ToBeDeleted[ToBeDeletedSize++] = (StarIndex);
//        }
    }

    // Go from the front back so that the index doesn't change
    for (int ToDeleteIndex = (ToBeDeletedSize - 1); ToDeleteIndex >= 0; ToDeleteIndex--)
    {
        //CurrentStars.erase(CurrentStars.begin() + ToBeDeleted[ToDeleteIndex]);
        //for(uint32_t i=0; ToBeDeleted[ToDeleteIndex]; i++)
        {
            if(CurrentStarsSize<=0) break;
            for(uint32_t kk=ToBeDeleted[ToDeleteIndex]; kk<(CurrentStarsSize-1); kk++)
            {
                memcpy(&CurrentStars[kk], &CurrentStars[kk+1], sizeof(CurrentStars[0]));
            }
            CurrentStarsSize--;
            break;
        }
    }
}
/*************************************************************************************************\
|        Sunrise [9]                                                                              |
\*************************************************************************************************/
void cledMatrixSunrise_Init(void)    // 日出   // Fail
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 20;
    mParams.Slider2Val = 30;
    mParams.shape = 0;
    mParams.progress = 0.f;
}
void cledMatrixSunrise(const uint32_t cols, const uint32_t rows)    // 日出   // Fail
{
    ;
}
/*************************************************************************************************\
|        SwirlCircles( [9]                                                                              |
\*************************************************************************************************/
static hsv_t hsv1;
static hsv_t hsv2;
static int radius = 0;
static void SwirlCircles_SetRandomColorsEnabled(int RandomColorsEnabled)
{
    //RandomColorsEnabled = value;

    if(!RandomColorsEnabled)
    {
        //ResetUserColors();
        rgb2hsv(UserColors[0], &hsv1);
        rgb2hsv(UserColors[1], &hsv2);
    }
    else
    {
        hsv1.hue = 0;
        hsv1.value = 255;
        hsv1.saturation = 255;

        hsv2.hue = 180;
        hsv2.value = 255;
        hsv2.saturation = 255;
    }
}

void cledMatrixSwirlCircles_Init(void)    // 漩涡圈
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 50;
    mParams.Slider2Val = 50;
    mParams.shape = 0;
    mParams.progress = 0.f;
    for(uint32_t i=0; i<(sizeof(UserColors)/sizeof(UserColors[0])); i++)
    {
        UserColors[i] = RandomRGBColor();
    }
    SwirlCircles_SetRandomColorsEnabled(1);
}
static uint32_t SwirlCircles_GetColor(unsigned int x, unsigned int y, unsigned int w, unsigned int h, float x1, float y1)
{
    float glow = 0.01 * mParams.Slider2Val;

    double distance1 = sqrt(pow(x1 - x, 2) + pow(y1 - y, 2));
    float distance1_percent = distance1 < radius ? 0 : pow(distance1 / (h+w), glow);

    hsv_t hsv_tmp;

    hsv_tmp.value = hsv1.value * (1 - distance1_percent);
    hsv_tmp.hue = hsv1.hue;
    hsv_tmp.saturation = hsv1.saturation;

    uint32_t color1 = (hsv2rgb(&hsv_tmp));

    float y2 = h - y1;
    float x2 = w - x1;

    double distance2 = sqrt(pow(x2 - x, 2) + pow(y2 - y, 2));
    float distance2_percent = distance2 < radius ? 0 : pow(distance2 / (h+w), glow);

    hsv_tmp.value = hsv2.value * (1 - distance2_percent);
    hsv_tmp.hue = hsv2.hue;
    hsv_tmp.saturation = hsv2.saturation;

    uint32_t color2 = (hsv2rgb(&hsv_tmp));

    return Screen(color1, color2);
}
void cledMatrixSwirlCircles(const uint32_t cols, const uint32_t rows)    // 漩涡圈
{
    float hx = 0.5 * cols;
    float hy = 0.5 * rows;
    int reverse=0;

    float y1 = hy + hy * sin(reverse ? -mParams.progress : mParams.progress);
    float x1 = hx + hx * cos(reverse ? -mParams.progress : mParams.progress);

    for(unsigned int h = 0; h < rows; h++)
    {
        for(unsigned int w = 0; w <  cols; w++)
        {
            uint32_t color = SwirlCircles_GetColor(w, h, cols, rows, x1, y1);

            //unsigned int led_num = map[h * width + w];
            //int led_num = h * cols + w;
            //controller_zone->SetLED(start_idx + led_num,color, Brightness);
            ugui_pset(w, h, color);
        }
    }
    mParams.progress +=  0.1 * (float) mParams.Speed / (float) mParams.FPS;

    //if(RandomColorsEnabled)
    {
        hsv1.hue++;
        hsv2.hue++;
    }
}
/*************************************************************************************************\
|        Wavy [9]                                                                              |
\*************************************************************************************************/
static int       Dir = 1 ;
static float      SineProgress      = 10.0f;
static float      WaveProgress      = 60.0f;

static float      WaveFrequency     = 2;
static float      WaveSpeed         = 30;
static float      OscillationSpeed  = 50;
static const double PI = 3.1415926;//std::atan(1)*4;
static uint32_t   RandomColors[2];
static void Wavy_GenerateRandomColors()
{
    //RandomColors.clear();

    uint32_t color = /*ColorUtils::*/RandomRGBColor();

    //RandomColors.push_back(color);
    //RandomColors.push_back(ColorUtils::Invert(color));
    RandomColors[0] = color;
    RandomColors[1] = Invert(color);
}
void cledMatrixWavy_Init(void)    // 波形的
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 20;
    mParams.Slider2Val = 30;
    mParams.shape = 0;
    mParams.progress = 0.f;
    Wavy_GenerateRandomColors();
}
uint32_t Wavy_GetColor(int i, int count)
{
    float pos = (i + (count * WaveProgress) / 100.0f)/count;
    float rad = (360.0f * pos) * PI / 180.0f;
    float wave_height = SineProgress * sin(WaveFrequency * rad);
    float h = 0.5 + wave_height/2;

    //RandomColorsEnabled = true;
    //if(RandomColorsEnabled)
    {
        return /*ColorUtils::*/Interpolate(RandomColors[0], RandomColors[1], h);
    }
//    else
//    {
//        return ColorUtils::Interpolate(UserColors[0], UserColors[1], h);
//    }
}
void cledMatrixWavy(const uint32_t cols, const uint32_t rows)    // 波形的
{
    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
        uint32_t color = Wavy_GetColor(col_id, cols);

        for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
            //int LedID = controller_zone->controller->zones[controller_zone->zone_idx].matrix_map->map[((row_id * cols) + col_id)];
            //int LedID = (row_id * cols) + col_id;
            //controller_zone->SetLED(start_idx + LedID, color, Brightness);
            ugui_pset(col_id, row_id, color);
        }
    }
    if(Dir)
    {
        if(SineProgress < 1)
        {
            SineProgress += 0.01 * OscillationSpeed / mParams.FPS;
        }
        else
        {
            Dir = 0;//false;
            SineProgress -= 0.01 * OscillationSpeed / mParams.FPS;
        }

    }
    else
    {
        if(SineProgress > -1)
        {
            SineProgress -= 0.01 * OscillationSpeed / mParams.FPS;

        }
        else
        {
            Dir = 1;//true;
            SineProgress += 0.01 * OscillationSpeed / mParams.FPS;
        }
    }

    if(/*RandomColorsEnabled && */SineProgress >= -0.01f && SineProgress <= 0.01f)
    {
        Wavy_GenerateRandomColors();
    }

    //SineProgress = min(SineProgress, 1.0f);
    //SineProgress = max(SineProgress, -1.0f);
    if(SineProgress>1.0f) SineProgress=1.0f;
    if(SineProgress<-1.0f) SineProgress=-1.0f;

    if(WaveProgress < 100)
    {
        WaveProgress += 0.05 * WaveSpeed / mParams.FPS;
    } else {
        WaveProgress = 0.0;
    }
}
/*************************************************************************************************\
|        ZigZag [9]                                                                              |
\*************************************************************************************************/
void cledMatrixZigZag_Init(void)    // 之字形的
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 10;
    mParams.Slider2Val = 30;
    mParams.shape = 0;
    mParams.progress = 0.f;
}
static uint32_t ZigZag_GetColor(float x, float y, float w, float h)
{
    float total_leds = w * h;
    float current_led_position = ((int) x % 2 == 0 ? y : h - y - 1 ) + x * h;
    float current_led_percent = current_led_position/total_leds;

    if(current_led_percent < mParams.progress)
    {
        float distance = pow(current_led_percent / mParams.progress, 3);

        hsv_t hsv;

        //if(RandomColorsEnabled)
        {
            hsv.hue = (int)(distance*360. -100.*mParams.time) % 360;
            hsv.saturation = 255;
            hsv.value = 255;
        }
//        else
//        {
//            rgb2hsv(UserColors[0], &hsv);
//        }

        hsv.value *= distance;
        return (hsv2rgb(&hsv));
    }
    else
    {
        return 0x00;//ColorUtils::OFF();
    }

}
void cledMatrixZigZag(const uint32_t cols, const uint32_t rows)    // 之字形的
{
    int reverse = 1;
    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
            uint32_t color = ZigZag_GetColor(reverse ? cols - col_id - 1: col_id, reverse ? rows - row_id - 1: row_id, cols, rows);
            //int LedID = controller_zones[i]->controller->zones[controller_zones[i]->zone_idx].matrix_map->map[((row_id * cols) + col_id)];
            //int LedID = (row_id * cols) + col_id;
            //controller_zone->SetLED(start_idx + LedID, color, Brightness);
            ugui_pset(col_id, row_id, color);
        }
    }

    mParams.time += 0.01 * (float) mParams.Speed / (float) mParams.FPS;
    mParams.progress = 2*(mParams.time-(long)mParams.time);
}
